package com.black.cat.jvm.instructions.references;


import com.black.cat.jvm.instructions.base.InstructionIndex16;
import com.black.cat.jvm.instructions.base.MethodInvokeLogic;
import com.black.cat.jvm.rtda.Frame;
import com.black.cat.jvm.rtda.JObject;
import com.black.cat.jvm.rtda.heap.constantpool.MethodRef;
import com.black.cat.jvm.rtda.heap.constantpool.RunTimeConstantPool;
import com.black.cat.jvm.rtda.heap.methodarea.JClass;
import com.black.cat.jvm.rtda.heap.methodarea.JMethod;
import com.black.cat.jvm.rtda.heap.methodarea.MethodLookup;

//invokespecial指令用来调用无须动态绑定的实例方法，包括构造函数、私有方法和通过super关键字调用的超类方法。
public class INVOKE_SPECIAL extends InstructionIndex16 {

    @Override
    public void execute(Frame frame) {

        //先拿到当前类、当前常量池、方法符号引用，然后解析符号引用，拿到解析后的类和方法
        JClass currentClass = frame.method().clazz();
        RunTimeConstantPool runTimeConstantPool = currentClass.constantPool();
        MethodRef methodRef = (MethodRef) runTimeConstantPool.getConstants(this.idx);
        JClass resolvedClass = methodRef.resolvedClass();
        JMethod resolvedMethod = methodRef.resolvedMethod();
        //假定从方法符号引用中解析出来的类是C，方法是M。如果M是构造函数，则声明M的类必须是C，否则抛出NoSuchMethodError异常
        if ("<init>".equals(resolvedMethod.name()) && resolvedMethod.clazz() != resolvedClass) {
            throw new NoSuchMethodError();
        }
        //如果是静态方法
        if (resolvedMethod.isStatic()) {
            throw new IncompatibleClassChangeError();
        }
        //从操作数栈中弹出this引用，如果该引用是null，抛出NullPointerException异常。
        JObject ref = frame.operandStack().getRefFromTop(resolvedMethod.getArgSlotCount() - 1);
        if (null == ref) {
            throw new NullPointerException();
        }

        //上面的判断确保protected方法只能被声明该方法的类或子类调用。
        if (resolvedMethod.isProtected() &&
                resolvedMethod.clazz().isSubClassOf(currentClass) &&
                !resolvedMethod.clazz().getPackageName().equals(currentClass.getPackageName()) &&
                ref.clazz() != currentClass &&
                !ref.clazz().isSubClassOf(currentClass)) {
            throw new IllegalAccessError();
        }

        JMethod methodToBeInvoked = resolvedMethod;
        //如果调用的中超类中的函数，但不是构造函数，且当前类的ACC_SUPER标志被设置，需要一个额外的过程查找最终要调用的
        // 方法；否则前面从方法符号引用中解析出来的方法就是要调用的方法
        if (currentClass.isSuper() &&
                resolvedClass.isSubClassOf(currentClass) &&
                !resolvedMethod.name().equals("<init>")) {
            methodToBeInvoked = MethodLookup.lookupMethodInClass(currentClass.superJClass, methodRef.getName(), methodRef.getDescriptor());
        }

        if (methodToBeInvoked == null || methodToBeInvoked.isAbstract()) {
            throw new AbstractMethodError();
        }

        MethodInvokeLogic.invokeMethod(frame, methodToBeInvoked);
    }

}